import Flutter
import UIKit
import IMKit
import PromiseKit

public class SwiftPinChatPlugin: NSObject, FlutterPlugin {
    // MARK: - Property
    private var viewController: UIViewController?
    
    // MARK: - Init
    init(viewController: UIViewController?) {
        super.init()
        self.viewController = viewController
    }
    
    // MARK: - Public_M
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "com.feibi.chat/pin_chat", binaryMessenger: registrar.messenger())
        
        let viewController = UIApplication.shared.delegate?.window??.rootViewController
        let instance = SwiftPinChatPlugin(viewController: viewController)
        
        registrar.addMethodCallDelegate(instance, channel: channel)
    }
  
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        let arguments = call.arguments as? [String: Any]
        if call.method == "configure" {
            configure(arguments: arguments, result: result)
        } else if call.method == "connectAndStartChat" {
            connectAndStartChat(arguments: arguments, result: result)
        } else if call.method == "connect" {
            connect(arguments: arguments, result: result)
        } else if call.method == "startChat" {
            startChat(arguments: arguments, result: result)
        } else if call.method == "updateMyProfile" {
            updateMyProfile(arguments: arguments, result: result)
        } else if call.method == "goChatRoomList" {
            goChatRoomList(result: result)
        } else {
            result(FlutterMethodNotImplemented)
        }
    }
    

    // MARK: - Private_M
    /// 配置IMKit SDK
    private func configure(arguments: [String: Any]?, result: @escaping FlutterResult) {
        guard let args = arguments else {
            result(FlutterError.nilArgument)
            return
        }
        
        guard let clientKey = args["clientKey"] as? String else {
            result(FlutterError.failedArgumentField("clientKey", type: String.self))
            return
        }
        
        guard let serverUrl = args["serverUrl"] as? String else {
            result(FlutterError.failedArgumentField("serverUrl", type: String.self))
            return
        }
        
        IMKit.configure(clientKey: clientKey, chatServerURL: URL(string: serverUrl)!)
        result(nil)
    }
    
    /// 连接IMKit
    private func connect(arguments: [String: Any]?, result: @escaping FlutterResult) {
        guard let args = arguments else {
            result(FlutterError.nilArgument)
            return
        }
        
        guard let userId = args["userId"] as? String else {
            result(FlutterError.failedArgumentField("userId", type: String.self))
            return
        }
        
        let accessToken = args["accessToken"] as? String
        
        IMKit.clear()
        IMKit.connect(uid: userId, token: accessToken).done({ _ in
            result(nil)
        }).catch({ error in
            result(FlutterError.failedResult(message: error.localizedDescription))
        })
    }
    
    /// 开始一对一聊天
    private func startChat(arguments: [String: Any]?, result: @escaping FlutterResult) {
        guard let args = arguments else {
            result(FlutterError.nilArgument)
            return
        }
        
        guard let toUserId = args["toUserId"] as? String else {
            result(FlutterError.failedArgumentField("toUserId", type: String.self))
            return
        }
        
        IMCreateDirectChatTask().perform(invitee: toUserId).done({ [weak self] room in
            self?.goChatRoom(roomId: room.id)
            result(nil)
        }).catch({ error in
            result(FlutterError.failedResult(message: error.localizedDescription))
        })
    }
    
    /// 更新当前用户信息
    private func updateMyProfile(arguments: [String: Any]?, result: @escaping FlutterResult) {
        guard let args = arguments else {
            result(FlutterError.nilArgument)
            return
        }
        
        /// 昵称
        let nickname = args["nickname"] as? String
        /// 头像
        let avatarUrl = args["avatarUrl"] as? String
        var avatar: URL?
        if let avatarUrl = avatarUrl {
            avatar = URL(string: avatarUrl)
        }
        /// 描述
        let description = args["description"] as? String
        
        IMUpdateMyProfileTask().perform(nickname: nickname, avatarURL: avatar, description: description).done({_ in
            result(nil)
        }).catch({ error in
            result(FlutterError.failedResult(message: error.localizedDescription))
        })
    }
    
    /// 进入聊天室列表
    private func goChatRoomList(result: @escaping FlutterResult) {
        goChatRoomList();
        result(nil)
    }
    
    /// 连接IMKit并开始一对一聊天
    private func connectAndStartChat(arguments: [String: Any]?, result: @escaping FlutterResult) {
        guard let args = arguments else {
            result(FlutterError.nilArgument)
            return
        }
        
        guard let userId = args["userId"] as? String else {
            result(FlutterError.failedArgumentField("userId", type: String.self))
            return
        }
        
        guard let toUserId = args["toUserId"] as? String else {
            result(FlutterError.failedArgumentField("toUserId", type: String.self))
            return
        }
        
        /// token
        let accessToken = args["accessToken"] as? String
        /// 昵称
        let nickname = args["nickname"] as? String
        /// 头像
        let avatarUrl = args["avatarUrl"] as? String
        var avatar: URL?
        if let avatarUrl = avatarUrl {
            avatar = URL(string: avatarUrl)
        }
        /// 描述
        let description = args["description"] as? String
        
        IMKit.clear()
        
        if nickname == nil && avatar == nil {
            IMKit.connect(
                uid: userId,
                token: accessToken
            ).then({ user -> Promise<IMRoom> in
                return IMCreateDirectChatTask().perform(invitee: toUserId)
            }).done({ [weak self] room in
                self?.goChatRoom(roomId: room.id)
                result(nil)
            }).catch({ error in
                result(FlutterError.failedResult(message: error.localizedDescription))
            })
        } else {
            IMKit.connect(
                uid: userId,
                token: accessToken
            ).then({ _ -> Promise<IMUser> in
                return IMUpdateMyProfileTask().perform(nickname: nickname, avatarURL: avatar, description: description)
            }).then({ user -> Promise<IMRoom> in
                return IMCreateDirectChatTask().perform(invitee: toUserId)
            }).done({ [weak self] room in
                self?.goChatRoom(roomId: room.id)
                result(nil)
            }).catch({ error in
                result(FlutterError.failedResult(message: error.localizedDescription))
            })
        }
    }
    
    private func goChatRoom(roomId: String) {
        let chatRoomVc = UINavigationController(rootViewController: ChatRoomViewController(roomID: roomId))
        chatRoomVc.modalPresentationStyle = .fullScreen
        viewController?.present(chatRoomVc, animated: true)
    }
    
    private func goChatRoomList() {
        let chatRoomListVc = UINavigationController(rootViewController: ChatRoomListViewController())
        chatRoomListVc.modalPresentationStyle = .fullScreen
        viewController?.present(chatRoomListVc, animated: true)
    }
}

extension FlutterError {
    static let nilArgument = FlutterError(
        code: "argument.nil",
        message: "Expect an argument when invoking channel method, but it is nil.",
        details: nil
    )
    
    static func failedArgumentField<T>(_ fieldName: String, type: T.Type) -> FlutterError {
        return FlutterError(
            code: "argument.failedField",
            message: "Expect a `\(fieldName)` field with type <\(type)> in the argument, " +
                     "but it is missing or type not matched.",
            details: nil
        )
    }
    
    static func failedResult(message: String) -> FlutterError {
        return FlutterError(
            code: "result.failed",
            message: message,
            details: nil
        )
    }
}
